{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "747e0e8d",
   "metadata": {},
   "source": [
    "# BentoML Scikit-learn Tutorial\n",
    "\n",
    "\n",
    "This is a sample project demonstrating basic usage of [BentoML](https://github.com/bentoml) with\n",
    "Scikit-learn.\n",
    "\n",
    "In this project, we will train a classifier model using Scikit-learn and the Iris dataset, build\n",
    "a prediction service for serving the trained model via an HTTP server, and containerize the \n",
    "model server as a docker image for production deployment.\n",
    "\n",
    "\n",
    "Link to source code: https://github.com/bentoml/BentoML/tree/main/examples/quickstart\n",
    "\n",
    "### Install Dependencies\n",
    "\n",
    "Install required python packages:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "daa3cbef",
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install -r https://raw.githubusercontent.com/bentoml/BentoML/main/examples/quickstart/requirements.txt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b66e31f7",
   "metadata": {},
   "source": [
    "##  Model Training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eb526488",
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn import svm, datasets\n",
    "\n",
    "# Load training data\n",
    "iris = datasets.load_iris()\n",
    "X, y = iris.data, iris.target\n",
    "\n",
    "# Model Training\n",
    "clf = svm.SVC()\n",
    "clf.fit(X, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c114c75",
   "metadata": {},
   "source": [
    "Save the `clf` model instance to BentoML local model store:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e94ed449",
   "metadata": {},
   "outputs": [],
   "source": [
    "import bentoml\n",
    "\n",
    "bentoml.sklearn.save_model(\"iris_clf\", clf)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d613e57e",
   "metadata": {},
   "source": [
    "Models saved can be accessed via `bentoml models` CLI command:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7d771f04",
   "metadata": {},
   "outputs": [],
   "source": [
    "!bentoml models get iris_clf:latest"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5a876780",
   "metadata": {},
   "outputs": [],
   "source": [
    "!bentoml models list"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "672721c4",
   "metadata": {},
   "source": [
    "To verify that the saved model can be loaded correctly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "28ac794b",
   "metadata": {},
   "outputs": [],
   "source": [
    "loaded_model = bentoml.sklearn.load_model(\"iris_clf:latest\")\n",
    "\n",
    "loaded_model.predict([[5.9, 3.0, 5.1, 1.8]])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8fd3bf97",
   "metadata": {},
   "source": [
    "In BentoML, the recommended way of running ML model inference in serving is via Runner, which \n",
    "gives BentoML more flexibility in terms of how to schedule the inference computation, how to \n",
    "batch inference requests and take advantage of hardware resources available. Saved models can\n",
    "be loaded as a Runner instance as shown below:\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "83205567",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create a Runner instance:\n",
    "iris_clf_runner = bentoml.sklearn.get(\"iris_clf:latest\").to_runner()\n",
    "\n",
    "# Runner#init_local initializes the model in current process, this is meant for development and testing only:\n",
    "iris_clf_runner.init_local()\n",
    "\n",
    "# This should yield the same result as the loaded model:\n",
    "iris_clf_runner.predict.run([[5.9, 3.0, 5.1, 1.8]])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3fa68254",
   "metadata": {},
   "source": [
    "## Serving the model\n",
    "\n",
    "A simple BentoML Service that serves the model saved above looks like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "127aa3fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%writefile service.py\n",
    "import numpy as np\n",
    "import bentoml\n",
    "from bentoml.io import NumpyNdarray\n",
    "\n",
    "iris_clf_runner = bentoml.sklearn.get(\"iris_clf:latest\").to_runner()\n",
    "\n",
    "svc = bentoml.Service(\"iris_classifier\", runners=[iris_clf_runner])\n",
    "\n",
    "@svc.api(input=NumpyNdarray(), output=NumpyNdarray())\n",
    "def classify(input_series: np.ndarray) -> np.ndarray:\n",
    "    return iris_clf_runner.predict.run(input_series)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "203beeed",
   "metadata": {},
   "source": [
    "Note: using `%%writefile` here because `bentoml.Service` definition must be created in its own `.py` file\n",
    "\n",
    "Start a dev model server to test out the service defined above:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7523b58f",
   "metadata": {},
   "outputs": [],
   "source": [
    "!bentoml serve service.py:svc --reload"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3974e4ce",
   "metadata": {},
   "source": [
    "\n",
    "Open your web browser at http://127.0.0.1:3000 to view the Bento UI for sending test requests.\n",
    "\n",
    "You may also send request with `curl` command or any HTTP client, e.g.:\n",
    "\n",
    "```bash\n",
    "curl -X POST -H \"content-type: application/json\" --data \"[[5.9, 3, 5.1, 1.8]]\" http://127.0.0.1:3000/classify\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f1a8bcc",
   "metadata": {},
   "source": [
    "### Build Bento for deployment"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d6192cd5",
   "metadata": {},
   "source": [
    "Bento is the distribution format in BentoML which captures all the source code, model files, config\n",
    "files and dependency specifications required for running the service for production deployment. Think \n",
    "of it as Docker/Container designed for machine learning models.\n",
    "\n",
    "To begin with building Bento, create a `bentofile.yaml` under your project directory:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6458e417",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%writefile bentofile.yaml\n",
    "service: \"service.py:svc\"\n",
    "labels:\n",
    "  owner: bentoml-team\n",
    "  project: gallery\n",
    "include:\n",
    "- \"*.py\"\n",
    "python:\n",
    "  packages:\n",
    "    - scikit-learn\n",
    "    - pandas"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47505e3c",
   "metadata": {},
   "source": [
    "Next, run `bentoml build` from current directory to start the Bento build:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b7cab8b2",
   "metadata": {},
   "outputs": [],
   "source": [
    "!bentoml build"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4c159551",
   "metadata": {},
   "source": [
    "A new Bento is now built and saved to local Bento store. You can view and manage it via \n",
    "`bentoml list`,`bentoml get` and `bentoml delete` CLI command."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "81ed8b84",
   "metadata": {},
   "source": [
    "## Containerize and Deployment"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c215454",
   "metadata": {},
   "source": [
    "Bento is designed to be deployed to run efficiently in a variety of different environments.\n",
    "And there are lots of deployment options and tools as part of the BentoML eco-system, such as \n",
    "[Yatai](https://github.com/bentoml/Yatai) and [bentoctl](https://github.com/bentoml/bentoctl) for\n",
    "direct deployment to cloud platforms.\n",
    "\n",
    "In this guide, we will show you the most basic way of deploying a Bento, which is converting a Bento\n",
    "into a Docker image containing the HTTP model server.\n",
    "\n",
    "Make sure you have docker installed and docker deamon running, and run the following commnand:\n",
    "\n",
    "```bash\n",
    "bentoml containerize iris_classifier:latest\n",
    "```\n",
    "\n",
    "This will build a new docker image with all source code, model files and dependencies in place,\n",
    "and ready for production deployment. To start a container with this docker image locally, run:\n",
    "\n",
    "```bash\n",
    "docker run -p 3000:3000 iris_classifier:invwzzsw7li6zckb2ie5eubhd \n",
    "```\n",
    "\n",
    "## What's Next?\n",
    "\n",
    "- 👉 [Pop into our Slack community!](https://l.bentoml.com/join-slack) We're happy to help with any issue you face or even just to meet you and hear what you're working on.\n",
    "\n",
    "- Dive deeper into the [Core Concepts](https://docs.bentoml.com/en/latest/concepts/index.html) in BentoML\n",
    "- Learn how to use BentoML with other ML Frameworks at [Frameworks Guide](https://docs.bentoml.com/en/latest/frameworks/index.html) or check out other [gallery projects](https://github.com/bentoml/BentoML/tree/main/examples)\n",
    "- Learn more about model deployment options for Bento:\n",
    "  - [🦄️ Yatai](https://github.com/bentoml/Yatai): Model Deployment at scale on Kubernetes\n",
    "  - [🚀 bentoctl](https://github.com/bentoml/bentoctl): Fast model deployment on any cloud platform\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
